<?php

namespace Fize\Think\Develop;

use think\facade\App;
use think\helper\Str;
use think\Request;
use think\Response;

/**
 * 爬虫
 */
trait CrawlerTrait
{
    use InteractsWithPages;

    protected $currentUri;

    protected $serverVariables = [];

    protected $isSsl = false;

    /** @var  Response */
    protected $response;

    public function get($uri, $data, array $headers = [])
    {
        $server = $this->transformHeadersToServerVars($headers);
        $server['REQUEST_METHOD'] = "GET";
        $_SERVER['REQUEST_METHOD'] = "GET";

        $this->call('GET', $uri, $data, [], [], $server, null, $headers);

        return $this;
    }

    /**
     * @param              $uri
     * @param array|string $data 提交附加参数。
     * @param array        $headers
     * @param array        $files
     * @return $this
     */
    public function post($uri, $data = [], array $headers = [], $files = [])
    {
        $server = $this->transformHeadersToServerVars($headers);
        $server['REQUEST_METHOD'] = "POST";
        $_SERVER['REQUEST_METHOD'] = "POST";

        $this->call('POST', $uri, $data, [], $files, $server, null, $headers);

        return $this;
    }

    public function put($uri, $data = [], array $headers = [])
    {
        $server = $this->transformHeadersToServerVars($headers);
        $server['REQUEST_METHOD'] = "PUT";
        $_SERVER['REQUEST_METHOD'] = "PUT";

        $this->call('PUT', $uri, $data, [], [], $server, null, $headers);

        return $this;
    }

    public function delete($uri, $data = [], array $headers = [])
    {
        $server = $this->transformHeadersToServerVars($headers);

        $this->call('DELETE', $uri, $data, [], [], $server, null, $headers);

        return $this;
    }

    /**
     * 创建request 并附加参数
     * @param              $method  GET|POST|PUT||PATCH|DELETE
     * @param              $uri
     * @param array|string $parameters
     * @param array        $cookies
     * @param array        $files
     * @param array        $server
     * @return Request
     */
    public function createRequest($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [])
    {
        $request = new Request();
        $request->setUrl($uri);
        $request->setMethod($method);
        $request->setPathinfo($this->getPathinfo($uri));
        if (is_array($parameters)) {
            switch ($method) {
                case "GET":
                    $request->withGet($parameters);
                    break;
                case "PUT":
                case "DELETE":
                case "PATCH":
                    $request->withInput($parameters);
                    break;
                case "POST":
                    $request->withPost($parameters);
                    break;
            }
        } elseif (is_string($parameters)) {
            $request->withInput($parameters);
        }
        $request->withCookie($cookies);
        $request->withFiles($files);
        $request->withServer(array_replace($this->serverVariables, $server));
        return $request;
    }

    private function getPathinfo($url)
    {
        $path = parse_url($url);
        $path = $path["path"];
        if ($path && $path[0] == "/") {
            $path = substr($path, 1);
        }
        return $path;
    }

    public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null, $header = [])
    {
        $this->currentUri = $this->prepareUrlForRequest($uri);

        // 修正HOST丢失
        $host = parse_url($this->currentUri, PHP_URL_HOST);
        $server['HTTP_X_FORWARDED_HOST'] = $host;
        $server['HTTP_HOST'] = $host;

        // HTTPS
        if ($this->isSsl) {
            $server['HTTPS'] = 'on';
        }

        $request = $this->createRequest($method, $this->currentUri, $parameters, $cookies, $files, $server);
        $request->withHeader($header);

        $http = App::bind('request', $request)->http;
        $response = $http->run($request);
        $http->end($response);
        //$response = App::bind('request', $request)->http->run($request);

        return $this->response = $response;
    }

    /**
     * Format the given key and value into a JSON string for expectation checks.
     *
     * @param string $key
     * @param mixed  $value
     * @return string
     */
    protected function formatToExpectedJson($key, $value)
    {
        $expected = json_encode([$key => $value]);

        if (Str::startsWith($expected, '{')) {
            $expected = substr($expected, 1);
        }

        if (Str::endsWith($expected, '}')) {
            $expected = substr($expected, 0, -1);
        }

        return $expected;
    }

    protected function withServerVariables(array $server)
    {
        $this->serverVariables = $server;

        return $this;
    }

    protected function transformHeadersToServerVars(array $headers)
    {
        $server = [];
        $prefix = 'HTTP_';

        foreach ($headers as $name => $value) {
            $name = strtr(strtoupper($name), '-', '_');

            if (!Str::startsWith($name, $prefix) && 'CONTENT_TYPE' != $name) {
                $name = $prefix . $name;
            }

            $server[$name] = $value;
            $_SERVER[$name] = $value;
        }

        return $server;
    }
}
